package com.hk.commons.crypto;

import lombok.SneakyThrows;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomStringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;

/**
 * 对称加密 ： AES 算法
 *
 * @author Kevin
 */
public abstract class AesUtils {

    /**
     * algorithm ，为 AES
     */
    private static final String AES = "AES";

    static {
        // BouncyCastle是一个开源的加解密解决方案，主页在http://www.bouncycastle.org/
        // 不加会报错: Cannot find any provider supporting AES/ECB/PKCS7Padding
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成 AES Key
     *
     * @param length 数据块，如 128、192、256
     */
    public static String generatorKey(int length) throws Exception {
        var generator = KeyGenerator.getInstance(AES);
        generator.init(length);
        return Base64.encodeBase64String(generator.generateKey().getEncoded());
    }

    /**
     * 解密
     *
     * @param input          input
     * @param key            aesKey
     * @param iv             iv，在某些模式下才需要使用 iv
     * @param transformation 格式为: algorithm/mode/padding
     */
    @SneakyThrows
    public static String decrypt(String input, String key, IvParameterSpec iv, String transformation) {
        var keySpec = new SecretKeySpec(key.getBytes(), AES);
        var cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
        var bytes = cipher.doFinal(Base64.decodeBase64(input));
        return new String(bytes);
    }

    /**
     * 生成随机 偏移量(Iv)
     */
    public static IvParameterSpec randomIv() {
        return new IvParameterSpec(RandomStringUtils.randomAlphabetic(16).getBytes());
    }

    /**
     * 加密
     *
     * @param input          input
     * @param key            aesKey
     * @param iv             iv ，在某些模式下才需要使用 iv
     * @param transformation 格式为: algorithm/mode/padding
     */
    @SneakyThrows
    public static String encrypt(String input, String key, IvParameterSpec iv, String transformation) {
        var keySpec = new SecretKeySpec(key.getBytes(), AES);
        var cipher = Cipher.getInstance(transformation);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
        var bytes = cipher.doFinal(input.getBytes());
        return Base64.encodeBase64String(bytes);
    }
}
